package ru.study.jdbc.dao;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;

import ru.study.jdbc.entity.*;
import ru.study.jdbc.ext.OrmUtils;
import ru.study.jdbc.orm.*;

public class MySqlDAOImpl implements DAO {
	private Connection con;	

	private static MySqlDAOImpl instance;

	@Deprecated
	public void executeQuery() {
		
		try {
			Statement st = con.createStatement();
			String sql = "select * from st_student";
			ResultSet rs = st.executeQuery(sql);
			
			ResultSetMetaData  md = rs.getMetaData();
			int x = md.getColumnCount();
			
			Object s;
			while(rs.next()) {
				for (int i =1;i<=x;i++) {					
					s = rs.getObject(i);
					if (s != null)
						System.out.print(s+" "+s.getClass().getSimpleName() + " ");
				}
				System.out.println();
			}
			
			
		} catch (Exception e) {
		
			e.printStackTrace();
		}		
	}	
	
	private MySqlDAOImpl() throws SQLException   {
		try {
			Class.forName("com.mysql.jdbc.Driver");		
			System.out.println("Driver loaded");
			String url = "jdbc:mysql://localhost/st_student";
			String user = "root";
			String password = "root";			
			
			con = DriverManager.getConnection(url, user, password);		
			System.out.println("My SQL Connected");	
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static synchronized MySqlDAOImpl getInstance() throws SQLException {
		if (instance == null) {
			instance = new MySqlDAOImpl();
		}
		return instance;
	}

	@Override
	public <T extends AbstractEntity> T insert(T entity) {
		IOrm orm = OrmManager.getOrmByClass(entity.getClass());
		FactoryByOrm fbo = new FactoryByOrm(orm.getKeyField(),orm.getOrmTable());
		String sql = fbo.getInsertQuery(entity);	
		
		try {
			PreparedStatement prestmt = con.prepareStatement(sql);		
			
			int i=1;//счет. параметров
			for (Map.Entry<String,Map> entry : orm.getOrmTable().entrySet()) {
				String table = entry.getKey();
				Map<String,String> condition = entry.getValue();			
				
				for (Map.Entry<String,String> ent : condition.entrySet()) {						
					String fieldname = ent.getKey();
					prestmt = OrmUtils.setParamByValue(entity, fieldname, prestmt, i);						
					i++;
				}					
			}			
			
			int result = prestmt.executeUpdate();						
			prestmt.close();
			
			if (result == 1 ) {
				entity  =  (T) get(entity.getClass(),entity.getId());	
			}	else {
				entity = (T) new EmptyEntity();
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		
		return entity ;
	}

	@Override
	public <T extends AbstractEntity> T get(Class<T> type, Long id) {
		// получить пример строки из ресурса, здесь подставить только параметры		
		T entity =  (T) new EmptyEntity();
		try {			 
			IOrm orm = OrmManager.getOrmByClass(type);
			if (orm != null) {
				FactoryByOrm qf = new FactoryByOrm(orm.getKeyField(),orm.getOrmTable());
				String sql = qf.getSelectSqlQuery() + id;
				//System.out.println("get sql ="+sql);
				
				Statement stmt = con.createStatement();
				ResultSet rs = stmt.executeQuery(sql);
				while (rs.next()) {
					entity = type.getDeclaredConstructor(ResultSet.class).newInstance(rs);
				}	
				rs.close();
				stmt.close();				
			} else {
				throw new RuntimeException("ORM описание не заполнено");
			}
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		return entity;
	}

	@Override
	public <T extends AbstractEntity> T update(T entity) {		
						
			IOrm orm = OrmManager.getOrmByClass(entity.getClass());
			FactoryByOrm fbo = new FactoryByOrm(orm.getKeyField(),orm.getOrmTable());
			String sql = fbo.getUpdateSqlQuery(entity);		
			
			try {
				PreparedStatement prestmt = con.prepareStatement(sql);
				
				Class cls = entity.getClass();				
							
				int i=1;//счет. параметров
				for (Map.Entry<String,Map> entry : orm.getOrmTable().entrySet()) {
					String table = entry.getKey();
					Map<String,String> condition = entry.getValue();			
					
					for (Map.Entry<String,String> ent : condition.entrySet()) {						
						String fieldname = ent.getKey();
						prestmt = OrmUtils.setParamByValue(entity, fieldname, prestmt, i);						
						i++;
					}					
				}
				
				for (Map.Entry<String,Map> entry : orm.getKeyField().entrySet()) {
					String table = entry.getKey();
					Map<String,String> condition = entry.getValue();					
					for (Map.Entry<String,String> ent : condition.entrySet()) {
						String fieldname = ent.getKey();						
						prestmt =  OrmUtils.setParamByValue(entity, fieldname, prestmt, i); 
					}				
				}
				
				int result = prestmt.executeUpdate();				
				
				entity = (T) new EmptyEntity();
				
				if (result == 1 ) {
					entity =  (T) get(entity.getClass(),entity.getId());					
				}	else  if (result ==0) {
					entity = insert(entity);
				}

				prestmt.close();
			}
			catch (SQLException e) {			
					e.printStackTrace();
			}
		
		return entity;
	}

	@Override
	public <T extends AbstractEntity> T delete(T entity) {
		IOrm orm = OrmManager.getOrmByClass(entity.getClass());
		FactoryByOrm fbo = new FactoryByOrm(orm.getKeyField(),orm.getOrmTable());
		String sql = fbo.getDeleteSqlQuery(entity);
		
		PreparedStatement prestmt;
		try {
			prestmt = con.prepareStatement(sql);			
			
			for (Map.Entry<String,Map> entry : orm.getKeyField().entrySet()) {
				String table = entry.getKey();				
				Map<String,String> condition = entry.getValue();					
				for (Map.Entry<String,String> ent : condition.entrySet()) {
					String fieldname = ent.getKey();
					
					prestmt =  OrmUtils.setParamByValue(entity, fieldname, prestmt, 1); 
				}				
			}
			
			int result = prestmt.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return entity;
	}

	@Override
	public 	void closeConection() {		
		try {
			con.close();
		} catch (SQLException e) {		
			e.printStackTrace();
		} finally {
			System.out.println("Connection closed");
		}
	}

}
